#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/kobject.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>

MODULE_LICENSE("GPL");

MODULE_AUTHOR("wk");
MODULE_VERSION("0.1");

MODULE_DESCRIPTION("Linux kernel driver - hello world demo");


static char* name = "world";
module_param(name, charp, S_IRUGO);
MODULE_PARM_DESC(name,"name, type: char*, permission: S_IRUGO");


static struct kobject* kobj;
static struct kset* kset;

static struct attribute test_addr = {
	.name = "kobj_config",
	.mode = S_IRWXUGO,
};

static struct attribute *def_attrs[] = {
	&test_addr,
	NULL,
};


ssize_t kobj_show(struct kobject* kobj, struct attribute* attr, char* buf)
{
	printk(KERN_INFO "have show.\n");
	printk(KERN_INFO "attrname: %s.\n", attr->name);
	sprintf(buf, "%s\n", attr->name);
	return strlen(attr->name)+2;
}

ssize_t kobj_store(struct kobject* kobj, struct attribute* attr, const char* buf, size_t count)
{
	printk(KERN_INFO "have store.\n");
	printk(KERN_INFO "write: %s\n", buf);
	return count;
}

struct sysfs_ops obj_ops = {
	.show = kobj_show,
	.store = kobj_store,
};


void obj_release(struct kobject* obj)
{
	printk(KERN_INFO "obj_release");
}

struct kobj_type ktype = {
	.release = obj_release,
	.sysfs_ops = &obj_ops,
	.default_attrs = def_attrs,
};



static struct class *demo_class;
static int major = 0;

static int demo_open(struct inode* node, struct file* filep) 
{
	return 0;
}

static ssize_t demo_write(struct file* filep, const char __user* buf, size_t size, loff_t* off)
{
	return 0;
}

static int demo_release(struct inode* node, struct file* filep)
{
	return 0;
}

static struct file_operations demo_ops = {
	.owner = THIS_MODULE,
	.open = demo_open,
	.write = demo_write,
	.release = demo_release,
};


static int __init hello_world_init(void)
{
	int ret = 0;
	printk(KERN_INFO "hello world init!\n");

	kset = kset_create_and_add("hello_world_set", NULL, kernel_kobj->parent);

	if (!kset) {
		printk(KERN_ALERT "create kset failed!");
		kset_put(kset);
	}

	kobj = (struct kobject*)kzalloc(sizeof(*kobj), GFP_KERNEL);

	if (IS_ERR(kobj)) {
		printk(KERN_ALERT "alloc failed!");
		return -ENOMEM;
	}

	ret = kobject_init_and_add(kobj, &ktype, &(kset->kobj), "hello_world_kobj");
	if (ret) {
		kobject_put(kobj);
		kset_unregister(kset);
	}

	major = register_chrdev(0, "demo_dev", &demo_ops);
	demo_class = class_create(THIS_MODULE, "demo_class");
	device_create(demo_class, NULL, MKDEV(major, 0), NULL, "demo_device");

//	kobj->parent = &(kset->kobj);

	return 0;
}

static void __exit hello_world_exit(void)
{

	unregister_chrdev(major, "demo_dev");
	device_destroy(demo_class, MKDEV(major, 0));
	class_destroy(demo_class);

	kobject_put(kobj);
	kset_unregister(kset);
	printk(KERN_INFO "good bye! %s\n", name);
}


module_init(hello_world_init);
module_exit(hello_world_exit);
